package ch.unisi.inf.pfii.teamwhite.risk.core.player;

/**
 * 
 * @author Matteo Bellan: bellanm@lu.unisi.ch
 * @author Andrea Michelotti: micheloa@lu.unisi.ch
 * @author Ranganathan Ramasubramanian: ramasubr@lu.unisi.ch
 * 
 */

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;

import javax.swing.Timer;

import ch.unisi.inf.pfii.teamwhite.risk.buildings.DefensiveShield;
import ch.unisi.inf.pfii.teamwhite.risk.buildings.EldestCouncil;
import ch.unisi.inf.pfii.teamwhite.risk.buildings.SlaveryCamp;
import ch.unisi.inf.pfii.teamwhite.risk.buildings.TaxCenter;
import ch.unisi.inf.pfii.teamwhite.risk.core.AttackHandler;
import ch.unisi.inf.pfii.teamwhite.risk.core.Planet;
import ch.unisi.inf.pfii.teamwhite.risk.core.ships.Frigate;

public final class Ai extends Player {
	
	private Timer timerOne;
	private Timer timerTwo;
	private Timer timerThree;

	private Random random;
	private int counter;
	private AttackHandler attackHandler;
	
	private ArrayList<AiListener> listeners;

	public Ai(final String name) {
		super(name, "Ai");
		random = new Random();
		counter = 0;
		attackHandler = new AttackHandler();
		listeners = new ArrayList<AiListener>();
	}

//	This method set the timing of the turn of an Ai player
	public final void stopTimers(){
		timerOne.stop();
		timerTwo.stop();
		timerThree.stop();
	}

//	This method made the Ai player choose its two first planets
	public final void assignPlanets(final ArrayList<Planet> planets) {
		Planet firstPlanet = aiChooseOnePlanet(planets);
		firstPlanet.setCapital(true);
		Planet secondPlanet = null;
		if (firstPlanet.getNeighbours().size() > 0) {
			secondPlanet = aiChooseOnePlanet(firstPlanet.getNeighbours());
		} else {
			secondPlanet = aiChooseOnePlanet(planets);
		}
		conquerPlanet(firstPlanet);
		firstPlanet.createShip();
		conquerPlanet(secondPlanet);
		secondPlanet.createShip();
	}

//	This method manage the turn of an Ai and invokes all the methods which computes all the calculation
	public final void play(final ArrayList<Planet> planets, final String name, final int number) {
		timerOne = new Timer(1000, new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				aiBuild(planets);
				aiBuyShip(planets);
				fireAiStep();
			}
		});
		
		timerOne.start();
		
		timerTwo = new Timer(500, new ActionListener(){
			public void actionPerformed(ActionEvent e) {
				aiMoveShips(planets, "", 0);
				fireAiStep();
			}
		});
		
		timerTwo.start();
		
		timerThree = new Timer(1000, new ActionListener(){
			public void actionPerformed(ActionEvent e) {
				aiLastMovement(planets);
				fireTurnFinished();
			}
		});
		
		timerThree.start();		
	}
	
//	This method decides which building put on the territories of the Ai player
	private final void aiBuild(final ArrayList<Planet> planets) {
		for (Planet planet : planets) {
			int numberOfEnemies = getNumberOfEnemyNeighbours(planet);
			if (numberOfEnemies >= 2) {
				planet.addBuilding(new DefensiveShield());
			} else {
				int otherBuilding = random.nextInt(2);
				switch (otherBuilding) {
				case 0:
					planet.addBuilding(new SlaveryCamp());
					break;
				case 1:
					planet.addBuilding(new TaxCenter());
					break;
				}
			}
			if (planet.isCapital()) {
				if (planet.getBuilding().getName() != "Eldest Council") {
					planet.addBuilding(new EldestCouncil());
				}
			}
		}
	}
	
//	This method decides where to put the ships on the territories of the Ai player
	private final void aiBuyShip(final ArrayList<Planet> planets) {
		int potentialShips = getMoney() / Frigate.getCost();
		if (potentialShips >= 1) {
			ArrayList<Planet> mostDangerousPlanets = new ArrayList<Planet>();
			for (Planet planet : planets) {
				int numberOfEnemies = getNumberOfEnemyNeighbours(planet);
				int dangerRate = planet.getNeighbours().size() - numberOfEnemies;
				if (dangerRate == 0) {
					for (int i = 0; i < potentialShips; i++) {
						planet.createShip();
						setMoney(getMoney() - Frigate.getCost());
					}
				} else if (dangerRate == 1) {
					mostDangerousPlanets.add(planet);
				} else if (dangerRate == 2) {
					mostDangerousPlanets.add(planet);
				}
			}
			if (mostDangerousPlanets.size() == 0) {
				aiCreateRandomShips(planets, potentialShips);
			} else {
				for (Planet planet : mostDangerousPlanets) {
					if (getMoney() < Frigate.getCost()) {
						return;
					} else {
						planet.createShip();
						setMoney(getMoney() - Frigate.getCost());
					}
				}
			}
		}
	}
	
//	This method decides where to attack
	public final void aiMoveShips(final ArrayList<Planet> planets, final String destinationPlanet, final int numberOfShips) {
		for (int i = 0; i < planets.size(); i++) {
			for (Planet neighbour : planets.get(i).getNeighbours()) {
				if (neighbour.getOwner() != this && !neighbour.getOwner().getName().equals("Gaia")) {
					if (planets.get(i).getShips().size() > 5) {
						int attackingShips = planets.get(i).getShips().size() - 1;
						moveShips(planets.get(i), neighbour, attackingShips);
						attackHandler.handleAttack(this, attackingShips,
								neighbour.getShips().size() - attackingShips,
								neighbour);
					}
				} else if (neighbour.getOwner().getName().equals("Gaia")) {
					if (planets.get(i).getShips().size() > 1) {
						int attackingShips = planets.get(i).getShips().size() - 1;
						moveShips(planets.get(i), neighbour, attackingShips);
						attackHandler.handleAttack(this, attackingShips,
								neighbour.getShips().size() - attackingShips,
								neighbour);
					} else {
					}
				}
			}
		}
	}

//	This method conquer a planet changing the owner and adding it to the Ai player territory
	public final void conquerPlanet(final Planet planet) {
		planet.setOwner(this);
		getTerritory().add(planet);
	}
	
//	This method decides where to move the ships of the Ai player before pass the turn
	public final void aiLastMovement(final ArrayList<Planet> planets) {
		try {
			int randomPlanet = random.nextInt(planets.size());
			Planet currentPlanet = planets.get(randomPlanet);
			int randomNeighbour = random.nextInt(currentPlanet.getNeighbours().size());
			Planet currentNeighbour = currentPlanet.getNeighbours().get(randomNeighbour);
			if (currentPlanet.getShips().size() > 1 && currentNeighbour.getOwner() == this) {
				moveShips(currentPlanet, currentNeighbour, currentPlanet.getShips().size() - 1);
				counter = 0;
			} else if (counter < 5) {
				counter++;
				aiLastMovement(planets);
			}
		} catch (IllegalArgumentException e) {
		}
	}

//	This method how many enemies there are around a planet
	private final int getNumberOfEnemyNeighbours(final Planet planet) {
		int numberOfEnemies = 0;
		for (Planet neighbour : planet.getNeighbours()) {
			if (!neighbour.getOwner().equals(this)
					&& !neighbour.getOwner().getName().equals("Gaia")) {
				numberOfEnemies++;
			}
		}
		return numberOfEnemies;
	}

//	This method creates ships randomly on the planets of the Ai player
	private final void aiCreateRandomShips(final ArrayList<Planet> planets, final int potentialShips) {
		try {
			for (int i = 0; i < potentialShips; i++) {
				int planetIndex = random.nextInt(planets.size());
				planets.get(planetIndex).createShip();
				setMoney(getMoney() - Frigate.getCost());
			}
		} catch (IllegalArgumentException e) {
		}
	}

//	This method decides where to put the first planet during the assignment phase
	private final Planet aiChooseOnePlanet(final ArrayList<Planet> planets) {
		ArrayList<Planet> availablePlanets = new ArrayList<Planet>();
		for (Planet planet : planets) {
			if (planet.getOwner().getName().equals("Gaia")) {
				availablePlanets.add(planet);
			}
		}
		int firstPlanetNumber = 0;
		
		try {
			firstPlanetNumber = random.nextInt(availablePlanets.size());
		} catch (Exception e) {
		}
		Planet choosenPlanet = availablePlanets.get(firstPlanetNumber);
		return choosenPlanet;
	}
	
//	Listeners
	public final void addAiListener(final AiListener li){
		listeners.add(li);
	}
	
	public void removeAiListener(final AiListener li){
		listeners.remove(li);
	}
	
//	Fire methods
	private void fireTurnFinished(){
		for (final AiListener li : listeners) {
			li.turnFinished();
		}
	}
	
	private void fireAiStep(){
		for (final AiListener li : listeners) {
			li.aiStep();
		}
	}
	
}